#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
远传水表619协议解析器
解析水表主动上报的报文数据
"""

import struct
from datetime import datetime
from typing import Dict, Any, Optional, List


def parse_water_meter_report(hex_data: str) -> Dict[str, Any]:
    """
    解析远传水表619协议的上报报文
    
    Args:
        hex_data: 十六进制字符串格式的报文数据
        
    Returns:
        解析后的数据字典
    """
    # 去除空格并转换为字节数组
    hex_data = hex_data.replace(' ', '')
    if len(hex_data) % 2 != 0:
        raise ValueError("十六进制数据长度必须为偶数")
    
    data = bytes.fromhex(hex_data)
    
    if len(data) < 17:
        raise ValueError("报文长度不足")
    
    result = {}
    
    # 解析帧头部分
    result.update(parse_frame_header(data))
    
    # 解析数据域
    data_start = 16  # 数据域开始位置
    data_length = result['data_length']
    
    if len(data) < data_start + data_length + 2:  # +2 for checksum and end frame
        raise ValueError("报文长度与数据长度字段不匹配")
    
    data_domain = data[data_start:data_start + data_length]
    result.update(parse_data_domain(data_domain))
    
    # 验证校验码和结束帧
    checksum_pos = data_start + data_length
    end_frame_pos = checksum_pos + 1
    
    result['checksum'] = data[checksum_pos]
    result['end_frame'] = data[end_frame_pos]
    
    # 验证校验码
    calculated_checksum = calculate_checksum(data[:checksum_pos])
    result['checksum_valid'] = (calculated_checksum == result['checksum'])
    
    # 验证结束帧
    result['end_frame_valid'] = (result['end_frame'] == 0x16)
    
    return result


def parse_frame_header(data: bytes) -> Dict[str, Any]:
    """
    解析报文帧头
    
    Args:
        data: 报文字节数据
        
    Returns:
        帧头解析结果
    """
    result = {}
    
    # 起始帧 (位置0)
    result['start_frame'] = data[0]
    result['start_frame_valid'] = (data[0] == 0x68)
    
    # 表类型 (位置1)
    result['meter_type'] = data[1]
    meter_types = {
        0x10: "冷水水表",
        0x11: "生活热水水表", 
        0x12: "直饮水水表",
        0x13: "中水水表",
        0x20: "热量表（计热量）",
        0x21: "热量表（计冷量）",
        0x30: "燃气表",
        0x40: "电度表"
    }
    result['meter_type_desc'] = meter_types.get(data[1], "未知类型")
    
    # 表地址 (位置2-8, 7字节BCD码)
    meter_addr_bytes = data[2:9]
    result['meter_address_raw'] = meter_addr_bytes.hex().upper()
    result['meter_address'] = parse_bcd_address(meter_addr_bytes)
    
    # 设备类型 (位置9)
    result['device_type'] = data[9]
    result['device_type_valid'] = (data[9] == 0x03)
    
    # 控制码 (位置10)
    result['control_code'] = data[10]
    control_codes = {
        0x97: "表端主动上报",
        0x17: "服务器对表端上报的响应",
        0x01: "服务器对表端下发读指令",
        0x81: "表端对服务器读指令的响应",
        0x04: "服务器对表端下发写指令",
        0x84: "表端对服务器写指令的响应"
    }
    result['control_code_desc'] = control_codes.get(data[10], "未知控制码")
    
    # 指令编号 (位置11-12, 高字节在前)
    result['command_id'] = struct.unpack('>H', data[11:13])[0]
    
    # 上报类型 (位置13-14, 高字节在前)
    report_type_raw = struct.unpack('>H', data[13:15])[0]
    result['report_type'] = report_type_raw
    result['report_type_flags'] = parse_report_type_flags(report_type_raw)
    
    # 数据长度 (位置15)
    result['data_length'] = data[15]
    
    return result


def parse_bcd_address(addr_bytes: bytes) -> str:
    """
    解析BCD码表地址
    
    Args:
        addr_bytes: 7字节地址数据
        
    Returns:
        解析后的地址字符串
    """
    # 将字节转换为BCD字符串，低字节在前
    bcd_str = ""
    for byte in addr_bytes:
        bcd_str = f"{byte:02X}" + bcd_str  # 低字节在前，所以倒序
    
    return bcd_str


def parse_report_type_flags(report_type: int) -> Dict[str, bool]:
    """
    解析上报类型标志位
    
    Args:
        report_type: 上报类型值
        
    Returns:
        各标志位状态
    """
    flags = {}
    flags['data_retransmit'] = bool(report_type & (1 << 14))  # D14: 数据重发
    flags['dense_sampling'] = bool(report_type & (1 << 13))   # D13: 密集采样
    flags['flow_over_limit'] = bool(report_type & (1 << 12))  # D12: 流量超限
    flags['daily_history'] = bool(report_type & (1 << 11))    # D11: 每日历史数据
    flags['overdraft_used_up'] = bool(report_type & (1 << 10)) # D10: 透支用完
    flags['arrears_alarm'] = bool(report_type & (1 << 9))     # D9: 欠费报警
    flags['remaining_alarm'] = bool(report_type & (1 << 8))   # D8: 剩余告警
    
    return flags


def parse_data_domain(data_domain: bytes) -> Dict[str, Any]:
    """
    解析数据域
    
    Args:
        data_domain: 数据域字节数据
        
    Returns:
        数据域解析结果
    """
    if len(data_domain) < 1:
        raise ValueError("数据域长度不足")
    
    result = {}
    
    # 数据包类型 (第一个字节)
    packet_type = data_domain[0]
    result['packet_type'] = packet_type
    
    packet_types = {
        0x01: "预付费普通表上报信息",
        0x02: "预付费阶梯表上报信息", 
        0x03: "后付费普通表上报信息",
        0x04: "暖通阀普通表上报信息",
        0x05: "额外扣费／最低消费上报信息",
        0x06: "每日历史数据上报信息"
    }
    result['packet_type_desc'] = packet_types.get(packet_type, "未知数据包类型")
    
    # 根据数据包类型解析具体数据
    if packet_type == 0x02:  # 预付费阶梯表上报信息
        result.update(parse_prepaid_ladder_data(data_domain[1:]))
    elif packet_type == 0x03:  # 后付费普通表上报信息
        result.update(parse_postpaid_data(data_domain[1:]))
    else:
        result['data_content'] = data_domain[1:].hex().upper()
        result['note'] = f"暂不支持解析数据包类型 {packet_type:02X}"
    
    return result


def parse_prepaid_ladder_data(data: bytes) -> Dict[str, Any]:
    """
    解析预付费阶梯表上报数据（数据包类型02）
    
    Args:
        data: 数据包内容（不包含数据包类型字节）
        
    Returns:
        解析结果
    """
    if len(data) < 77:  # 预付费阶梯表数据至少需要77字节
        raise ValueError("预付费阶梯表数据长度不足")
    
    result = {}
    pos = 0
    
    # 厂商代码 (2字节)
    result['manufacturer_code'] = struct.unpack('>H', data[pos:pos+2])[0]
    pos += 2
    
    # 表类型代码 (2字节)
    result['meter_type_code'] = struct.unpack('>H', data[pos:pos+2])[0]
    pos += 2
    
    # 硬件版本 (2字节)
    result['hardware_version'] = f"{data[pos+1]:02X}.{data[pos]:02X}"
    pos += 2
    
    # 软件版本 (2字节)
    result['software_version'] = f"{data[pos+1]:02X}.{data[pos]:02X}"
    pos += 2
    
    # IMEI (8字节)
    result['imei'] = data[pos:pos+8].hex().upper()
    pos += 8
    
    # ICCID (10字节)
    result['iccid'] = data[pos:pos+10].hex().upper()
    pos += 10
    
    # 电压 (2字节, 单位0.1V)
    voltage_raw = struct.unpack('>H', data[pos:pos+2])[0]
    result['voltage'] = voltage_raw / 10.0
    pos += 2
    
    # 信号功率 RSRP (1字节)
    result['rsrp'] = data[pos] if data[pos] != 0xFF else None
    pos += 1
    
    # 信号质量 RSRQ (1字节)  
    result['rsrq'] = data[pos] if data[pos] != 0xFF else None
    pos += 1
    
    # 信噪比 SNR (1字节)
    result['snr'] = data[pos] if data[pos] != 0xFF else None
    pos += 1
    
    # 累计上报次数 (2字节)
    result['total_report_count'] = struct.unpack('>H', data[pos:pos+2])[0]
    pos += 2
    
    # 当日上报次数 (2字节)
    result['daily_report_count'] = struct.unpack('>H', data[pos:pos+2])[0]
    pos += 2
    
    # 上报模式 (6字节)
    result['report_mode'] = data[pos:pos+6].hex().upper()
    pos += 6
    
    # 状态字 (2字节)
    status_word = struct.unpack('>H', data[pos:pos+2])[0]
    result['status_word'] = status_word
    result['status_flags'] = parse_status_flags(status_word)
    pos += 2
    
    # 购买次数 (1字节)
    result['purchase_count'] = data[pos]
    pos += 1
    
    # 累计金额 (4字节, 单位分)
    result['total_amount'] = struct.unpack('<I', data[pos:pos+4])[0] / 100.0
    pos += 4
    
    # 剩余金额 (4字节, 单位分)
    result['remaining_amount'] = struct.unpack('<I', data[pos:pos+4])[0] / 100.0
    pos += 4
    
    # 总购买金额 (4字节, 单位分)
    result['total_purchase_amount'] = struct.unpack('<I', data[pos:pos+4])[0] / 100.0
    pos += 4
    
    # 日用量 (4字节, 单位升)
    result['daily_usage'] = struct.unpack('<I', data[pos:pos+4])[0] / 1000.0
    pos += 4
    
    # 月用量 (4字节, 单位升)
    result['monthly_usage'] = struct.unpack('<I', data[pos:pos+4])[0] / 1000.0
    pos += 4
    
    # 累计用量 (4字节, 单位升)
    result['total_usage'] = struct.unpack('<I', data[pos:pos+4])[0] / 1000.0
    pos += 4
    
    # 当前阶梯 (1字节)
    result['current_ladder'] = data[pos]
    pos += 1
    
    # 当前阶梯价格 (2字节, 单位分/升)
    result['current_ladder_price'] = struct.unpack('>H', data[pos:pos+2])[0] / 100.0
    pos += 2
    
    # 实时时钟 (6字节 BCD码: 秒分时日月年)
    if pos + 6 <= len(data):
        clock_data = data[pos:pos+6]
        result['real_time_clock'] = parse_bcd_datetime(clock_data)
        pos += 6
    
    return result


def parse_postpaid_data(data: bytes) -> Dict[str, Any]:
    """
    解析后付费普通表上报数据（数据包类型03）
    
    Args:
        data: 数据包内容（不包含数据包类型字节）
        
    Returns:
        解析结果
    """
    if len(data) < 61:  # 后付费普通表数据至少需要61字节
        raise ValueError("后付费普通表数据长度不足")
    
    result = {}
    pos = 0
    
    # 厂商代码 (2字节)
    result['manufacturer_code'] = struct.unpack('>H', data[pos:pos+2])[0]
    pos += 2
    
    # 表类型代码 (2字节)
    result['meter_type_code'] = struct.unpack('>H', data[pos:pos+2])[0]
    pos += 2
    
    # 硬件版本 (2字节)
    result['hardware_version'] = f"{data[pos+1]:02X}.{data[pos]:02X}"
    pos += 2
    
    # 软件版本 (2字节)
    result['software_version'] = f"{data[pos+1]:02X}.{data[pos]:02X}"
    pos += 2
    
    # IMEI (8字节)
    result['imei'] = data[pos:pos+8].hex().upper()
    pos += 8
    
    # ICCID (10字节)
    result['iccid'] = data[pos:pos+10].hex().upper()
    pos += 10
    
    # 电压 (2字节, 单位0.01V)
    voltage_raw = struct.unpack('>H', data[pos:pos+2])[0]
    result['voltage'] = voltage_raw / 100.0
    pos += 2
    
    # 信号功率 RSRP (1字节)
    result['rsrp'] = data[pos] if data[pos] != 0xFF else None
    pos += 1
    
    # 信号质量 RSRQ (1字节)  
    result['rsrq'] = data[pos] if data[pos] != 0xFF else None
    pos += 1
    
    # 信噪比 SNR (1字节)
    result['snr'] = data[pos] if data[pos] != 0xFF else None
    pos += 1
    
    # 累计上报次数 (2字节)
    result['total_report_count'] = struct.unpack('>H', data[pos:pos+2])[0]
    pos += 2
    
    # 当日上报次数 (2字节)
    result['daily_report_count'] = struct.unpack('>H', data[pos:pos+2])[0]
    pos += 2
    
    # 上报模式 (6字节)
    result['report_mode'] = data[pos:pos+6].hex().upper()
    pos += 6
    
    # 状态字 (2字节)
    status_word = struct.unpack('>H', data[pos:pos+2])[0]
    result['status_word'] = status_word
    result['status_flags'] = parse_status_flags(status_word)
    pos += 2
    
    # 累计用量 (4字节, 单位升)
    result['total_usage'] = struct.unpack('>I', data[pos:pos+4])[0] / 1000.0
    pos += 4
    
    # 日用量 (4字节, 单位升)
    result['daily_usage'] = struct.unpack('>I', data[pos:pos+4])[0] / 1000.0
    pos += 4
    
    # 月用量 (4字节, 单位升)
    result['monthly_usage'] = struct.unpack('>I', data[pos:pos+4])[0] / 1000.0
    pos += 4
    
    # 实时时钟 (6字节 BCD码: 秒分时日月年)
    if pos + 6 <= len(data):
        clock_data = data[pos:pos+6]
        result['real_time_clock'] = parse_bcd_datetime(clock_data)
        pos += 6
    
    return result


def parse_status_flags(status_word: int) -> Dict[str, bool]:
    """
    解析状态字标志位
    
    Args:
        status_word: 状态字值
        
    Returns:
        各状态标志位
    """
    flags = {}
    # 根据协议文档定义的状态位进行解析
    # 这里需要根据具体协议文档补充状态位定义
    flags['valve_status'] = bool(status_word & 0x01)
    flags['low_battery'] = bool(status_word & 0x02)
    flags['magnetic_interference'] = bool(status_word & 0x04)
    # 可根据需要继续添加其他状态位
    
    return flags


def parse_bcd_datetime(bcd_data: bytes) -> str:
    """
    解析BCD码时间
    
    Args:
        bcd_data: 6字节BCD码时间数据 (秒分时日月年)
        
    Returns:
        格式化的时间字符串
    """
    if len(bcd_data) != 6:
        return "时间数据长度错误"
    
    try:
        second = bcd_to_int(bcd_data[0])
        minute = bcd_to_int(bcd_data[1])
        hour = bcd_to_int(bcd_data[2])
        day = bcd_to_int(bcd_data[3])
        month = bcd_to_int(bcd_data[4])
        year = bcd_to_int(bcd_data[5]) + 2000  # 假设年份为21世纪
        
        return f"{year:04d}-{month:02d}-{day:02d} {hour:02d}:{minute:02d}:{second:02d}"
    except:
        return "时间解析错误"


def bcd_to_int(bcd_byte: int) -> int:
    """
    BCD码转整数
    
    Args:
        bcd_byte: BCD码字节
        
    Returns:
        对应的整数值
    """
    return (bcd_byte >> 4) * 10 + (bcd_byte & 0x0F)


def calculate_checksum(data: bytes) -> int:
    """
    计算校验码
    
    Args:
        data: 需要校验的数据
        
    Returns:
        校验码
    """
    return sum(data) & 0xFF


def format_parse_result(result: Dict[str, Any]) -> str:
    """
    格式化解析结果为可读字符串
    
    Args:
        result: 解析结果字典
        
    Returns:
        格式化的字符串
    """
    lines = []
    lines.append("=" * 60)
    lines.append("远传水表619协议报文解析结果")
    lines.append("=" * 60)
    
    # 帧头信息
    lines.append("\n【帧头信息】")
    lines.append(f"起始帧: 0x{result['start_frame']:02X} ({'有效' if result['start_frame_valid'] else '无效'})")
    lines.append(f"表类型: 0x{result['meter_type']:02X} ({result['meter_type_desc']})")
    lines.append(f"表地址: {result['meter_address']} (原始: {result['meter_address_raw']})")
    lines.append(f"设备类型: 0x{result['device_type']:02X} ({'有效' if result['device_type_valid'] else '无效'})")
    lines.append(f"控制码: 0x{result['control_code']:02X} ({result['control_code_desc']})")
    lines.append(f"指令编号: {result['command_id']}")
    lines.append(f"上报类型: 0x{result['report_type']:04X}")
    
    # 上报类型标志位
    flags = result['report_type_flags']
    active_flags = [k for k, v in flags.items() if v]
    if active_flags:
        lines.append(f"上报标志: {', '.join(active_flags)}")
    
    lines.append(f"数据长度: {result['data_length']} 字节")
    
    # 数据域信息
    lines.append(f"\n【数据域信息】")
    lines.append(f"数据包类型: 0x{result['packet_type']:02X} ({result['packet_type_desc']})")
    
    # 根据数据包类型显示详细信息
    if result['packet_type'] == 0x02 or result['packet_type'] == 0x03:
        lines.append(f"\n【设备信息】")
        lines.append(f"厂商代码: {result['manufacturer_code']}")
        lines.append(f"表类型代码: {result['meter_type_code']}")
        lines.append(f"硬件版本: {result['hardware_version']}")
        lines.append(f"软件版本: {result['software_version']}")
        lines.append(f"IMEI: {result['imei']}")
        lines.append(f"ICCID: {result['iccid']}")
        
        lines.append(f"\n【信号和电源信息】")
        lines.append(f"电压: {result['voltage']:.1f}V")
        if result['rsrp'] is not None:
            lines.append(f"信号功率(RSRP): {result['rsrp']}")
        if result['rsrq'] is not None:
            lines.append(f"信号质量(RSRQ): {result['rsrq']}")
        if result['snr'] is not None:
            lines.append(f"信噪比(SNR): {result['snr']}")
        
        lines.append(f"\n【上报统计】")
        lines.append(f"累计上报次数: {result['total_report_count']}")
        lines.append(f"当日上报次数: {result['daily_report_count']}")
        lines.append(f"上报模式: {result['report_mode']}")
        
        lines.append(f"\n【状态信息】")
        lines.append(f"状态字: 0x{result['status_word']:04X}")
        status_flags = result['status_flags']
        active_status = [k for k, v in status_flags.items() if v]
        if active_status:
            lines.append(f"状态标志: {', '.join(active_status)}")
        
        # 预付费阶梯表特有信息
        if result['packet_type'] == 0x02:
            lines.append(f"\n【费用信息】")
            lines.append(f"购买次数: {result['purchase_count']}")
            lines.append(f"累计金额: {result['total_amount']:.2f}元")
            lines.append(f"剩余金额: {result['remaining_amount']:.2f}元")
            lines.append(f"总购买金额: {result['total_purchase_amount']:.2f}元")
            
            lines.append(f"\n【阶梯信息】")
            lines.append(f"当前阶梯: {result['current_ladder']}")
            lines.append(f"当前阶梯价格: {result['current_ladder_price']:.2f}元/立方米")
        
        lines.append(f"\n【用量信息】")
        lines.append(f"日用量: {result['daily_usage']:.3f}立方米")
        lines.append(f"月用量: {result['monthly_usage']:.3f}立方米")
        lines.append(f"累计用量: {result['total_usage']:.3f}立方米")
        
        lines.append(f"\n【时间信息】")
        lines.append(f"实时时钟: {result['real_time_clock']}")
    
    # 校验信息
    lines.append(f"\n【校验信息】")
    lines.append(f"校验码: 0x{result['checksum']:02X} ({'有效' if result['checksum_valid'] else '无效'})")
    lines.append(f"结束帧: 0x{result['end_frame']:02X} ({'有效' if result['end_frame_valid'] else '无效'})")
    
    lines.append("=" * 60)
    
    return "\n".join(lines)


if __name__ == "__main__":
    # 测试用例：用户提供的报文数据
    test_data = "68 10 38 04 19 06 25 52 82 03 97 00 00 00 01 3E 03 C1 FC 00 21 01 13 01 05 86 99 16 07 35 58 55 0D 89 86 06 25 39 00 06 90 20 93 0D F2 8D F2 FD 00 5D 00 01 C0 05 A0 FF FF FF 00 00 00 00 C7 06 00 00 00 00 00 00 00 00 25 09 28 09 23 09 1A 16"
    
    try:
        result = parse_water_meter_report(test_data)
        print(format_parse_result(result))
    except Exception as e:
        print(f"解析错误: {e}")
        import traceback
        traceback.print_exc()